#include "StdAfx.hpp"
#include "VertexDecl.hpp"
#include "Framework.hpp"

using namespace common;

namespace {

const uint HEADER_MAGIC = MAKEFOURCC('V', 'D', 'C', 'L');
const uint HEADER_VERSION = 1;

} // namespace <anonymous>

//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// VertexDecl

D3DVERTEXELEMENT9 const VertexDecl::END_ELEM = {0xFF,0,D3DDECLTYPE_UNUSED, 0,0,0}; // D3DDECL_END
D3DVERTEXELEMENT9 const VertexDecl::INVALID_ELEM = { 0xFFFF, 0xFFFF, 0xFF, 0xFF, 0xFF, 0xFF };

int VertexDecl::Cmp(const D3DVERTEXELEMENT9 &e1, const D3DVERTEXELEMENT9 &e2)
{
	return memcmp(&e1, &e2, sizeof(D3DVERTEXELEMENT9));
}

int VertexDecl::Cmp(const VertexDecl &d1, const VertexDecl &d2)
{
	uint c1 = d1.GetCount(), c2 = d2.GetCount();
	int cmpr;
	for (uint i = 0; ; i++)
	{
		if (i == c1 && i == c2)
			return 0;
		else if (i == c1)
			return -1;
		else if (i == c2)
			return 1;

		cmpr = Cmp(d1[i], d2[i]);
		if (cmpr != 0)
			return cmpr;
	}
}

uint VertexDecl::GetElemSize(BYTE ElemType)
{
	switch (ElemType)
	{
	case D3DDECLTYPE_FLOAT1:    return sizeof(float);
	case D3DDECLTYPE_FLOAT2:    return sizeof(float) * 2;
	case D3DDECLTYPE_FLOAT3:    return sizeof(float) * 3;
	case D3DDECLTYPE_FLOAT4:    return sizeof(float) * 4;
	case D3DDECLTYPE_D3DCOLOR:  return sizeof(D3DCOLOR);
	case D3DDECLTYPE_UBYTE4:    return 4;
	case D3DDECLTYPE_SHORT2:    return sizeof(short) * 2;
	case D3DDECLTYPE_SHORT4:    return sizeof(short) * 4;
	case D3DDECLTYPE_UBYTE4N:   return 4;
	case D3DDECLTYPE_SHORT2N:   return sizeof(short) * 2;
	case D3DDECLTYPE_SHORT4N:   return sizeof(short) * 4;
	case D3DDECLTYPE_USHORT2N:  return sizeof(unsigned short) * 2;
	case D3DDECLTYPE_USHORT4N:  return sizeof(unsigned short) * 4;
	case D3DDECLTYPE_UDEC3:     return 4;
	case D3DDECLTYPE_DEC3N:     return 4;
	case D3DDECLTYPE_FLOAT16_2: return 4;
	case D3DDECLTYPE_FLOAT16_4: return 8;
	case D3DDECLTYPE_UNUSED:    return 0;
	default:
		assert(false);
		return 0;
	}
}

uint VertexDecl::TypeToComponentCount(BYTE ElemType)
{
	switch (ElemType)
	{
	case D3DDECLTYPE_FLOAT1:    return 1;
	case D3DDECLTYPE_FLOAT2:    return 2;
	case D3DDECLTYPE_FLOAT3:    return 3;
	case D3DDECLTYPE_FLOAT4:    return 4;
	case D3DDECLTYPE_D3DCOLOR:  return 4;
	case D3DDECLTYPE_UBYTE4:    return 4;
	case D3DDECLTYPE_SHORT2:    return 2;
	case D3DDECLTYPE_SHORT4:    return 4;
	case D3DDECLTYPE_UBYTE4N:   return 4;
	case D3DDECLTYPE_SHORT2N:   return 2;
	case D3DDECLTYPE_SHORT4N:   return 4;
	case D3DDECLTYPE_USHORT2N:  return 2;
	case D3DDECLTYPE_USHORT4N:  return 4;
	case D3DDECLTYPE_UDEC3:     return 3;
	case D3DDECLTYPE_DEC3N:     return 3;
	case D3DDECLTYPE_FLOAT16_2: return 2;
	case D3DDECLTYPE_FLOAT16_4: return 4;
	default: return 0;
	}
}

void VertexDecl::SaveToStream(common::Stream &s) const
{
	s.WriteEx(GetCount());
	s.Write(&m_Elems[0], GetCount() * sizeof(D3DVERTEXELEMENT9));
}

void VertexDecl::LoadFromStream(common::Stream &s)
{
	uint Count;
	s.ReadEx(&Count);

	SetCount(Count);
	s.MustRead(&m_Elems[0], Count * sizeof(D3DVERTEXELEMENT9));
}

void VertexDecl::SetCount(uint ElemCount)
{
	uint OldCount = GetCount();
	if (ElemCount == OldCount) return;

	m_Elems.resize(ElemCount+1);
	
	for (uint i = OldCount; i < ElemCount; i++)
		m_Elems[i] = INVALID_ELEM;

	m_Elems[ElemCount] = END_ELEM;
}

uint VertexDecl::CalcRealElemCount(const D3DVERTEXELEMENT9 *Elems, uint Count)
{
	for (uint i = 0; i < Count; i++)
		if (Elems[i] == END_ELEM)
			return i;
	return Count;
}

uint VertexDecl::CalcRealElemCount(const D3DVERTEXELEMENT9 *ElemBeg, const D3DVERTEXELEMENT9 *ElemEnd)
{
	uint i;
	const D3DVERTEXELEMENT9 *ei;
	for (i = 0, ei = ElemBeg; ei < ElemEnd; ei++, i++)
		if (*ei == END_ELEM)
			return i;
	return ElemEnd - ElemBeg;
}

void VertexDecl::Set(const D3DVERTEXELEMENT9 &SingleElem)
{
	assert(SingleElem != END_ELEM);

	m_Elems.resize(2);
	m_Elems[0] = SingleElem;
	m_Elems[1] = END_ELEM;
}

void VertexDecl::Add(const D3DVERTEXELEMENT9 &SingleElem)
{
	assert(SingleElem != END_ELEM);

	uint Count = GetCount();
	m_Elems.resize(Count+2);
	m_Elems[Count] = SingleElem;
	m_Elems[Count+1] = END_ELEM;
}

void VertexDecl::Insert(uint Index, const D3DVERTEXELEMENT9 &SingleElem)
{
	assert(SingleElem != END_ELEM);

	m_Elems.insert(m_Elems.begin() + Index, SingleElem);
}

uint VertexDecl::GetVertexSize() const
{
	uint Count = GetCount();
	if (Count == 0) return 0;
	return Get(Count-1).Offset + GetElemSize(m_Elems[Count-1].Type);
}

bool VertexDecl::IsSingleStream() const
{
	uint Count = GetCount();
	for (uint i = 0; i < Count; i++)
		if (m_Elems[i].Stream != 0)
			return false;
	return true;
}

uint VertexDecl::RecalcOffsets()
{
	uint Count = GetCount(), Offset = 0;
	for (uint i = 0; i < Count; i++)
	{
		m_Elems[i].Offset = (WORD)Offset;
		Offset += GetElemSize(m_Elems[i].Type);
	}
	return Offset;
}

void VertexDecl::InternalSet(const D3DVERTEXELEMENT9 *Elems, uint Count)
{
	m_Elems.resize(Count+1);
	for (uint i = 0; i < Count; i++)
		m_Elems[i] = Elems[i];
	m_Elems[Count] = END_ELEM;
}

void VertexDecl::InternalAdd(const D3DVERTEXELEMENT9 *Elems, uint Count)
{
	uint OldCount = GetCount();
	m_Elems.resize(OldCount + Count + 1);
	for (uint src_i = 0, dst_i = OldCount; src_i < Count; src_i++, dst_i++)
		m_Elems[dst_i] = m_Elems[src_i];
	m_Elems[OldCount + Count] = END_ELEM;
}

void VertexDecl::InternalInsert(uint Index, const D3DVERTEXELEMENT9 *Elems, uint Count)
{
	m_Elems.insert(m_Elems.begin() + Index, Elems, Elems + Count);
}

void VertexDecl::Delete(uint Index)
{
	m_Elems.erase(m_Elems.begin() + Index);
}

void VertexDecl::Delete(uint IndexBeg, uint IndexEnd)
{
	assert(IndexEnd >= IndexBeg);
	if (IndexEnd == IndexBeg) return;
	uint Count = GetCount();
	if (IndexBeg >= Count) return;
	if (IndexEnd > Count)
		IndexEnd = Count;
	m_Elems.erase(m_Elems.begin() + IndexBeg, m_Elems.begin() + IndexEnd);
}

uint VertexDecl::FindByUsage(BYTE Usage) const
{
	uint Count = GetCount();
	for (uint i = 0; i < Count; i++)
		if (m_Elems[i].Usage == Usage)
			return i;
	return MAXUINT4;
}

uint VertexDecl::FindByUsage(BYTE Usage, BYTE UsageIndex) const
{
	uint Count = GetCount();
	for (uint i = 0; i < Count; i++)
		if (m_Elems[i].Usage == Usage && m_Elems[i].UsageIndex == UsageIndex)
			return i;
	return MAXUINT4;
}

uint VertexDecl::FindByUsage(BYTE Usage, BYTE UsageIndex, WORD Stream) const
{
	uint Count = GetCount();
	for (uint i = 0; i < Count; i++)
		if (m_Elems[i].Usage == Usage && m_Elems[i].UsageIndex == UsageIndex && m_Elems[i].Stream == Stream)
			return i;
	return MAXUINT4;
}

uint VertexDecl::FindNextUsageIndex(BYTE Usage) const
{
	uint NextUsageIndex = 0, Count = GetCount();
	for (uint i = 0; i < GetCount(); i++)
		if (m_Elems[i].Usage == Usage)
			if (m_Elems[i].UsageIndex >= NextUsageIndex)
				NextUsageIndex = m_Elems[i].UsageIndex + 1;
	return NextUsageIndex;
}

void VertexDecl::SetByUsage(uint ElemCount, WORD Stream, const BYTE *UsageArr, const BYTE *TypeArr, const BYTE *UsageIndexArr)
{
	m_Elems.resize(ElemCount+1);
	m_Elems[ElemCount] = END_ELEM;

	BYTE UsageIndices[14] = { 0 };
	uint Offset = 0;
	for (uint i = 0; i < ElemCount; i++)
	{
		m_Elems[i].Stream = Stream;
		m_Elems[i].Offset = Offset;
		m_Elems[i].Type = TypeArr[i];
		m_Elems[i].Method = D3DDECLMETHOD_DEFAULT;
		m_Elems[i].Usage = UsageArr[i];
		
		if (UsageIndexArr)
			m_Elems[i].UsageIndex = UsageIndexArr[i];
		else
			m_Elems[i].UsageIndex = UsageIndices[UsageArr[i]]++;

		Offset += GetElemSize(TypeArr[i]);
	}
}


//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// VertexDeclMngr

VertexDeclMngr *g_VertexDeclMngr = NULL;


VertexDeclMngr::VertexDeclMngr()
{
}

VertexDeclMngr::~VertexDeclMngr()
{
	Clear();
}

void VertexDeclMngr::Clear()
{
	m_Map.clear();
}

void VertexDeclMngr::OnLostDevice()
{
	Clear();
}

void VertexDeclMngr::OnResetDevice()
{
}

IDirect3DVertexDeclaration9 * VertexDeclMngr::GetD3dDecl(const VertexDecl &Decl)
{
	MAP_TYPE::iterator it = m_Map.find(Decl);
	if (it != m_Map.end())
		return it->second.get();

	IDirect3DVertexDeclaration9 *D3dDecl;
	HRESULT hr = frame::Dev->CreateVertexDeclaration(Decl.GetArray(), &D3dDecl);
	if (FAILED(hr))
		throw DirectXError(hr, "Cannot create Vertex Declaration", __FILE__, __LINE__);

	return m_Map.insert(MAP_TYPE::value_type(Decl, shared_ptr<IDirect3DVertexDeclaration9, ReleasePolicy>(D3dDecl))).first->second.get();
}
